﻿/+
版本：0.9.2 - 2009.2.12
+/
module libsocket;

import core.thread;
import core.sync.mutex;
import core.stdc.stdio;
import core.stdc.string;
import winapi;

import DataList;
import WinSocket;

const int MAX_BUFSIZE=4096;

enum SOC_TYPE:ubyte{
	SOC_TCPSERVER=0x01,//TCP服务器
	SOC_TCPCLIENT=0x02,//TCP客户端
	SOC_UDPSERVER=0x04,//UDP服务器
	SOC_SYSPORT  =0x00,//动态端口
	SOC_READ     =0x08,//读操作
	SOC_WRITE    =0x10,//写操作
	SOC_ACCEPT   =0x20,//连接请求
	SOC_CLOSE    =0x40,//连接被关闭
}

// 这是per-Handle数据.它包含了一个套节字的信息
struct HandleData{
	//由于D的特性这里为兼容windows必须设置为4字节对齐模式
	align(4):
	sockaddr_in addrLocal; // 连接的本地地址
	sockaddr_in addrRemote;// 连接的远程地址
	socket_t s;            // 套节字句柄
	uint  OutstandingSend; // 发送投递数
	uint  OutstandingRead; // 读取投递数
	ubyte SvrType;         // 服务类型
}

// 这是per-I/O数据.描述了每次IO数据的信息
struct IOData{
	align(4):
	WSAOVERLAPPED Overlapped;// 完成结构
	ubyte   op;              // 操作类型
	ubyte   buf[MAX_BUFSIZE];// 连接缓冲
	uint    len;             // 缓冲区实际长度
}
//服务器数据
struct ServerData{
	align(4):
	socket_t s;    //服务套接字
	TServerWork sw;//服务线程
}

//回调页面
interface TNetCall{
	int GetData(uint key,ubyte[] buf,uint len);
	int GetEevents(uint key,int Ev);
};

class TLibSocket{
public:
	this(TNetCall nc){
		if (nc is null)
			throw new Exception("a parameter error!");
		gSvrStart=false;
		gIOCP    =null;
		gWork    =null;
		gNC      =nc;
		gMutGHD  =new Mutex();
		gDLWork  =new TDataList();
		//初始化Winsock.dll
		WSADATA wsaData;
		if (WSAStartup(0x0202,&wsaData)!=0)
			throw new Exception("Open network error!");
	}

	~this(){
		if (gDLWork !is null)
			delete gDLWork;
		WSACleanup();
	}

	//启动IOCP服务
	bool Start(uint uTNum){
		//创建完成端口
		SYSTEM_INFO SysInfo;

		//检测服务是否已经开启
		if (gWork !is null)
			return false;
		
		GetSystemInfo(&SysInfo);
		//如果uTNum=0则默认为CPU个数的2倍
		if (!uTNum)
			uTNum=SysInfo.dwNumberOfProcessors*2;
		if (!gIOCP){
			gIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,null,0,uTNum);
			if (!gIOCP) return false;
		}
		//启动工作线程
		gWork=new TWork(this);
		gWork.start();
		gSvrStart=true;
		return gSvrStart;
	}
	bool Shutdown(){
		if (gSvrStart==false)
			return false;
		if (gIOCP !is null)
			PostQueuedCompletionStatus(gIOCP,-1,-1,null);
		while(gWork.isRunning())
			Sleep(100);
		if (gWork !is null){
			delete gWork;
			gWork=null;
		}
		//关闭所有网络连接
		gMutGHD.lock();
		while(true){
			HandleData *tmpHD;
			socket_t s;
			
			tmpHD=cast(HandleData*)gDLWork.drop(0);
			if (tmpHD is null){
				gMutGHD.unlock();
				break;
			}
			s=tmpHD.s;
			delete tmpHD;
			closesocket(s);
		}
		gMutGHD.unlock();
		return true;
	}
	//创建Socket
	//说明：当strAddress为0.0.0.0是表示任意IP地址
	//      当uPort为0是表示系统会自动分配端口
	uint CreateSocket(char[] strAddress,ushort uPort,ubyte uSvrType){
		sockaddr_in socClient;
		socket_t s;
		int nReturn;
		HANDLE hReturn;
		//参数检查
		if (strAddress.length<7)
			return 0;
		//创建套接字
		if (SOC_TYPE.SOC_UDPSERVER==uSvrType){
			s=WSASocketA(AddressFamily.INET,SocketType.DGRAM,ProtocolType.UDP,null,
				0,ExWSASocket.OVERLAPPED);
		}else if (SOC_TYPE.SOC_TCPCLIENT==uSvrType){
			s=WSASocketA(AddressFamily.INET,SocketType.STREAM,ProtocolType.TCP,null,
				0,ExWSASocket.OVERLAPPED);
		}else
			return 0;
		if (SocketEr.INVALID_SOCKET==s)
			return 0;
		socClient.sin_family=AddressFamily.INET;
		socClient.sin_port  =htons(uPort);
		socClient.sin_addr.s_addr=inet_addr(strAddress.ptr);
		switch(uSvrType){
		case SOC_TYPE.SOC_UDPSERVER:
			//绑定Socket
			nReturn=bind(s,cast(sockaddr*)&socClient,socClient.sizeof);
			if (SocketEr.SOCKET_ERROR==nReturn){
				closesocket(s);
				return 0;
			}
			break;
		case SOC_TYPE.SOC_TCPCLIENT:
			//创建连接
			nReturn=connect(s,cast(sockaddr*)&socClient,socClient.sizeof);
			if (SocketEr.SOCKET_ERROR==nReturn){
				closesocket(s);
				return 0;
			}
			break;
		default:
			closesocket(s);
			return 0;
			break;
		}
		//加入IOCP
		HandleData *tmpHD=new HandleData;
		tmpHD.s                       = s;
		tmpHD.addrLocal               = socClient;
		tmpHD.SvrType                 = uSvrType;
		tmpHD.OutstandingSend         = 0;
		tmpHD.OutstandingRead         = 0;
		//更新完成端口,如果失败则断开当前网络
		hReturn=CreateIoCompletionPort(cast(HANDLE)tmpHD.s,gIOCP,
			cast(ULONG_PTR)tmpHD.s,0);
		if (null==hReturn){
			delete tmpHD;
			closesocket(s);
			return 0;
		}
		//投递一个读操作
		IOData *ioData=new IOData;
		ioData.Overlapped.Internal     = 0;
		ioData.Overlapped.InternalHigh = 0;
		ioData.Overlapped.Offset       = 0;
		ioData.Overlapped.OffsetHigh   = 0;
		ioData.Overlapped.hEvent       = null;
		ioData.op                      = SOC_TYPE.SOC_READ;
		if (!PostRecv(tmpHD,ioData)){
			delete tmpHD;
			delete ioData;
			closesocket(s);
			return 0;
		}
		//向表中添加数据
		gMutGHD.lock();
		if (gDLWork.add(s,cast(void*)tmpHD)!=true){
			delete tmpHD;
			delete ioData;
			closesocket(s);
			gMutGHD.unlock();
			return 0;
		}
		gMutGHD.unlock();
		return s;
	}
	bool CloseSocket(uint key){
		HandleData *tmpHD;
		socket_t s;
		gMutGHD.lock();
		tmpHD=cast(HandleData*)gDLWork.drop(key);
		if (tmpHD is null){
			gMutGHD.unlock();
			return false;
		}
		s=tmpHD.s;
		delete tmpHD;
		closesocket(s);
		gMutGHD.unlock();
		return true;
	}
	bool SetBroadCast(uint key,bool op){
		HandleData *tmpHD=cast(HandleData*)gDLWork.GetData(key);
		if (tmpHD is null)
			return false;
		int bSet;
		if (op)
			bSet=1;
		else
			bSet=0;
        setsockopt(tmpHD.s,SocketOptionLevel.SOCKET,SocketOption.SO_BROADCAST,
            &bSet,bSet.sizeof);
		return true;
	}
	bool WriteData(uint key,char[] Address,ushort uPort,ubyte[] data){
		HandleData *tmpHD;
		IOData *ioData;
		
		if (Address.length<7 || data.length<1)
			return false;
		if (data.length>MAX_BUFSIZE)
			return false;
		gMutGHD.lock();
		tmpHD=cast(HandleData*)gDLWork.GetData(key);
		if (tmpHD is null){
			gMutGHD.unlock();
			return false;
		}
		gMutGHD.unlock();
		if (tmpHD.SvrType==SOC_TYPE.SOC_UDPSERVER){
			tmpHD.addrRemote.sin_family=AddressFamily.INET;
			tmpHD.addrRemote.sin_port  =htons(uPort);
			tmpHD.addrRemote.sin_addr.s_addr=inet_addr(Address.ptr);
			ioData=new IOData;
			ioData.Overlapped.Internal     = 0;
			ioData.Overlapped.InternalHigh = 0;
			ioData.Overlapped.Offset       = 0;
			ioData.Overlapped.OffsetHigh   = 0;
			ioData.Overlapped.hEvent       = null;
			memcpy(ioData.buf.ptr,data.ptr,data.length);
			ioData.len=data.length;
			if (PostSend(tmpHD,ioData)==false){
				delete ioData;
				return false;
			}
		}
		return true;
	}
	//说明:完成数据处理函数,该函数只被线程单独调用.
	bool HandleIO(socket_t key,DWORD dwTrans,IOData *ioData,int nError){
		HandleData *hd;
		gMutGHD.lock();
		hd    =cast(HandleData*)gDLWork.GetData(cast(uint)key);
		gMutGHD.unlock();
		if (hd is null || ioData is null){
			if (CloseSocket(key)==false)
				closesocket(key);
			if (gNC !is null)
				gNC.GetEevents(key,SOC_TYPE.SOC_CLOSE);
			return false;
		}
		
		if (nError!=0){
			if (CloseSocket(key)==false)
				closesocket(key);
			if (gNC !is null)
				gNC.GetEevents(key,SOC_TYPE.SOC_CLOSE);
			printf("error code=%d\n",nError);
			return false;
		}
		
		if (ioData.op==SOC_TYPE.SOC_WRITE){
			if (dwTrans==0){//Socket被关闭
				if (CloseSocket(key)==false)
					closesocket(key);
				if (gNC !is null)
					gNC.GetEevents(key,SOC_TYPE.SOC_CLOSE);
			}else{
				//通知用户
				if (gNC !is null)
					gNC.GetEevents(key,SOC_TYPE.SOC_WRITE);
			}
			delete ioData;
		}else if (ioData.op==SOC_TYPE.SOC_READ){//检查读操作
			if (dwTrans==0){//Socket被关闭
				delete ioData;
				if (CloseSocket(key)==false)
					closesocket(key);
				if (gNC !is null)
					gNC.GetEevents(key,SOC_TYPE.SOC_CLOSE);
			}else{
				ioData.len=dwTrans;
				//通知用户
				if (gNC !is null)
					gNC.GetData(key,ioData.buf,ioData.len);
				PostRecv(hd,ioData);
			}
			
		}
		return true;
	}
	HANDLE GetIOCP(){
		return gIOCP;
	}
	uint GetLinkNum(){
		return gDLWork.getsize();
	}
private:
	bool PostRecv(HandleData *lhd,IOData *lioData){
		int ClientLen;

		lioData.op=SOC_TYPE.SOC_READ;
		
		DWORD dwBytes =0;
		DWORD dwFlags =0;//MSG_PARTIAL;
		WSABUF buf;
		buf.buf=cast(char*)lioData.buf.ptr;
		buf.len=MAX_BUFSIZE;
		
		if (lhd.SvrType==SOC_TYPE.SOC_TCPCLIENT){
			int ret=WSARecv(lhd.s,&buf,1,&dwBytes,&dwFlags,&lioData.Overlapped,null);
			if (ret!=0){
				ret=WSAGetLastError();
				printf("tcp error=%d\n",ret);
				if (ret!=WSA_IO_PENDING){
					return false;
				}
			}
		}else if (lhd.SvrType==SOC_TYPE.SOC_UDPSERVER){
			ClientLen=lhd.addrRemote.sizeof;
			int ret=WSARecvFrom(lhd.s,&buf,cast(uint)1,&dwBytes,&dwFlags,
				cast(sockaddr*)&lhd.addrRemote,&ClientLen,&lioData.Overlapped,null);
			if (ret!=0){
				ret=WSAGetLastError();
				printf("udp error=%d\n",ret);
				if (ret!=WSA_IO_PENDING){
					return false;
				}
			}
		}
		return true;
	}
	bool PostSend(HandleData *lhd,IOData *lioData){
		int ClientLen;
		
		if (lhd.OutstandingSend>255){
			return false;
		}
		lioData.op=SOC_TYPE.SOC_WRITE;
		// 投递此重叠I/O
		DWORD dwBytes = 0;
		DWORD dwFlags = 0;
		WSABUF buf;
		buf.buf=cast(char*)lioData.buf.ptr;
		buf.len=lioData.len;
		
		if (lhd.SvrType==SOC_TYPE.SOC_TCPCLIENT){
			if (WSASend(lhd.s,&buf,1,&dwBytes,dwFlags,&lioData.Overlapped,null)!=0){
				if (WSAGetLastError()!=WSA_IO_PENDING)
					return false;
			}
		}else if (lhd.SvrType==SOC_TYPE.SOC_UDPSERVER){
			ClientLen=lhd.addrRemote.sizeof;
			if (WSASendTo(lhd.s,&buf,cast(uint)1,&dwBytes,dwFlags,
				cast(sockaddr*)&lhd.addrRemote,ClientLen,&lioData.Overlapped,null)!=0){
				if (WSAGetLastError()!=WSA_IO_PENDING)
					return false;
			}
		}

		return true;
	}
	ushort htons(ushort p){
		int ret=p;
		ret=ret<<8 | ((p>>8) & 0xFF);
		return ret & 0xFFFF;
	}
private://变量
	HANDLE gIOCP;     //完成端口句柄
	TWork  gWork;     //工作线程
	TDataList gDLWork;//socket表
	HandleData gHD;   //工作连接
	Mutex  gMutGHD;   //多线程访问限制
	TNetCall gNC;     //回调接口
	bool gSvrStart;   //服务器状态
}

class TWork:Thread{
	//这里类必须使用直接声明不可使用委托
	TLibSocket gLS;
	HANDLE     gIOCP;
	this(TLibSocket ls){
		gLS  =ls;
		gIOCP=gLS.GetIOCP;
		super(&run);
	}
	
	private void run(){
		DWORD dwKey;
		DWORD dwTrans;
		LPOVERLAPPED lpol;
		WSAOVERLAPPED ol;
		BOOL bOK;

		while(true){
			bOK=GetQueuedCompletionStatus(gIOCP,&dwTrans,&dwKey,&lpol,INFINITE);
			if (dwTrans==-1){//用户通知退出
				printf("thread exit\n");
				break;
			}
			int nError=0;
			if (bOK==FALSE){//出错处理
				SOCKET s;
				if (dwKey == 0){
					printf("thread exit\n");
					break;
				}
				s=cast(socket_t)dwKey;
				DWORD dwFlags = 0;
				if (!WSAGetOverlappedResult(s,&ol,&dwTrans,FALSE,&dwFlags))
					nError=WSAGetLastError();
				printf("dwTrans1=%d::%d::%d\n",dwTrans,dwFlags,nError);
			}
			try{
				IOData *lioData=cast(IOData *)lpol;
				synchronized{gLS.HandleIO(cast(socket_t)dwKey,dwTrans,lioData,nError);}
			}catch{
				printf("run HandleIO error\n");
				synchronized{gLS.CloseSocket(dwKey);}
			}
		}
		printf("thread closed\n");
	}
}

//TCP服务器类
class TLibServer{
public:
	this(TNetCall nc){
		gNC       =nc;
		gAutoClose=true;//默认自动关闭开启
	}
	uint CreateServer(char[] strAddress,ushort uPort,TNetCall nc){
		sockaddr_in socaddr;
		socket_t    s;
		int nReturn;

		s=WSASocketA(AddressFamily.INET,SocketType.STREAM,ProtocolType.TCP,null,
				0,ExWSASocket.OVERLAPPED);
		if (SocketEr.INVALID_SOCKET==s)
			return 0;
		socClient.sin_family          =AddressFamily.INET;
		socClient.sin_port            =htons(uPort);
		socClient.sin_addr.s_addr     =inet_addr(strAddress.ptr);
		//socClient.sin_addr.S_un.S_addr=INADDR_ANY;
    	//绑定Socket
    	nReturn=bind(s,cast(sockaddr*)&socaddr,socaddr.sizeof);
        if (SocketEr.SOCKET_ERROR==nReturn)
			return 0;
    	//监听端口
        if (SocketEr.SOCKET_ERROR==listen(s,5))
			return 0;
		//创建线程
		ServerData *sd=new ServerData;
		sd.s          =s;
		sd.sw         =new TServerWork(this,sd);
		//添加到列表中
		if (gDLWork.add(s,sd)!=true){
			delete sd.sw;
			delete sd;
			closesocket(s);
			return 0;
		}
		//启动服务
		sd.sw.start();
		return s;
	}
	bool Close(uint lkey){
	}
	//开启自动关闭时如果用户不接管服务上来的链接则服务会自动关闭该链接
	bool AutoClose(){
		return gAutoClose;
	}
	void AutoClose(bool ac){
		gAutoClose=ac;
	}
	//说明:完成数据处理函数,该函数只被线程单独调用.
	bool HandleIO(socket_t key,socket_t ioData,int nError){
	}
private:
	bool gAutoClose;  //自动关闭
	TNetCall gNC;     //回调接口
	TDataList gDLWork;//socket表
	Mutex  gMutGHD;   //多线程访问限制
}
//服务线程
//由于TCP服务必须阻塞式通讯所以这里
class TServerWork:Thread{
	this(TLibServer ls,ServerData sd){
		gLS=ls;
		gSD=sd;
		super(&run);
	}
private:
	void run(){
	}
private:
	TLibServer gLS;
	ServerData gSD;
}

